package com.vision.sales.collection.cl.ui;

import static com.vision.sales.cl.SalesServices.collectionSvc;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.SimpleBeanEditorDriver;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.Widget;
import com.sencha.gxt.core.client.Style.SelectionMode;
import com.sencha.gxt.data.client.loader.RpcProxy;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.Store;
import com.sencha.gxt.data.shared.loader.LoadEvent;
import com.sencha.gxt.data.shared.loader.LoadResultListStoreBinding;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.data.shared.loader.PagingLoadResult;
import com.sencha.gxt.data.shared.loader.PagingLoader;
import com.sencha.gxt.widget.core.client.TabItemConfig;
import com.sencha.gxt.widget.core.client.Window;
import com.sencha.gxt.widget.core.client.button.TextButton;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.event.HideEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.form.DateField;
import com.sencha.gxt.widget.core.client.form.NumberField;
import com.sencha.gxt.widget.core.client.form.NumberPropertyEditor;
import com.sencha.gxt.widget.core.client.form.PropertyEditor;
import com.sencha.gxt.widget.core.client.form.StoreFilterField;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.form.validator.MaxLengthValidator;
import com.sencha.gxt.widget.core.client.grid.ColumnConfig;
import com.sencha.gxt.widget.core.client.grid.ColumnModel;
import com.sencha.gxt.widget.core.client.grid.Grid;
import com.sencha.gxt.widget.core.client.grid.GridView;
import com.sencha.gxt.widget.core.client.menu.CheckMenuItem;
import com.sencha.gxt.widget.core.client.menu.Item;
import com.sencha.gxt.widget.core.client.menu.MenuItem;
import com.sencha.gxt.widget.core.client.toolbar.PagingToolBar;
import com.vision.core.cl.BaseModule;
import com.vision.core.cl.Callback;
import com.vision.core.cl.event.CreatedEvent;
import com.vision.core.cl.event.CreatedEvent.CreationHandler;
import com.vision.core.cl.ui.ConfirmDialogHandler;
import com.vision.core.cl.ui.Notifications;
import com.vision.core.cl.ui.UI;
import com.vision.core.cl.ui.component.BasicGridView;
import com.vision.core.cl.ui.component.BasicPagingLoader;
import com.vision.core.cl.ui.component.MultiSelectListAndDetailPanel;
import com.vision.core.cm.db.data.Company;
import com.vision.sales.cl.SalesServices;
import com.vision.sales.cl.SalesTasks;
import com.vision.sales.cl.data.CollectionItemProperties;
import com.vision.sales.cl.data.CollectionProperties;
import com.vision.sales.cl.event.CollectionCreatedEvent;
import com.vision.sales.cl.event.CollectionDeletedEvent;
import com.vision.sales.cl.resource.SalesResources;
import com.vision.sales.cl.ui.component.TransactionLoadConfig;
import com.vision.sales.cl.ui.component.TransactionNoCell;
import com.vision.sales.cl.ui.component.TransactionStatusCell;
import com.vision.sales.cm.data.TransactionStatus;
import com.vision.sales.cm.db.data.Collection;
import com.vision.sales.cm.db.data.Collection.FieldSpecs;
import com.vision.sales.cm.db.data.CollectionItem;
import com.vision.sales.cm.db.data.Customer;

/**
 * 
 * @author Mark
 *
 */
public class CollectionsPanel extends MultiSelectListAndDetailPanel<Collection> implements Editor<Collection> {

	interface Binder extends UiBinder<Widget, CollectionsPanel> { }

	private static final Binder binder = GWT.create(Binder.class);
	
	@Ignore @UiField VerticalLayoutContainer mainPanel;

	// Toolbar %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	@Ignore @UiField StoreFilterField<Collection> gridFilter;
	@Ignore @UiField TextButton filterBtn;
	@Ignore @UiField MenuItem statusMenuItem;
		@Ignore @UiField CheckMenuItem pendingOnlyMenuItem;
		@Ignore @UiField CheckMenuItem postedOnlyMenuItem;
		@Ignore @UiField CheckMenuItem allStatusMenuItem;
	
	@Ignore @UiField TextButton addBtn;
	@Ignore @UiField TextButton postBtn;
	@Ignore @UiField TextButton moreBtn;
	@Ignore @UiField MenuItem refreshMenuItem;
	@Ignore @UiField MenuItem deleteMenuItem;

	// Grid %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	@Ignore @UiField Grid<Collection> grid;
	@UiField PagingToolBar pagingToolbar;
	PagingLoader<PagingLoadConfig, PagingLoadResult<Collection>> loader;
	
	// Form %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	// Interface used to bind the form fields to the Collection fields
	interface CollectionDriver extends SimpleBeanEditorDriver<Collection, CollectionsPanel> { }
	private CollectionDriver driver = GWT.create(CollectionDriver.class);
	
	@UiField(provided = true) NumberPropertyEditor<Double> doublePropertyEditor = UI.doubleEditor();
	@UiField(provided = true) NumberFormat numberFormat = NumberFormat.getFormat("#,##0.00");
	
	@Ignore @UiField TextField companyTxt;
	@UiField TextField refNo;
	@UiField DateField date;
	@Ignore @UiField TextField customerTxt;
	@Ignore @UiField TextField paymentTypeTxt;
	@UiField TextField checkNo;
	@UiField DateField checkDate;
	@UiField TextField cashAccount;
	@UiField NumberField<Double> grossSales;
	@UiField NumberField<Double> overruns;
	@UiField NumberField<Double> overpayments;
	@UiField NumberField<Double> discounts;
	@UiField NumberField<Double> rejects;
	@UiField NumberField<Double> returns;
	@UiField NumberField<Double> subtotal;
	@UiField NumberField<Double> tax;
	@UiField NumberField<Double> netAmount;
	@Ignore @UiField TextField statusTxt;
	
	// Ignored by the form driver since these are not fields in the Collection
	@Ignore @UiField TextButton saveBtn;
	@Ignore @UiField TextButton cancelBtn;
	
	// Sales Invoices Grid %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	@UiField(provided = true) Grid<CollectionItem> collectionItemsGrid;
	
	// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	public CollectionsPanel() {
		// Global Handlers -- should be registered only once
		BaseModule.addHandler(CollectionCreatedEvent.TYPE, new CreationHandler<Collection>() {
			@Override
			public void onCreated(CreatedEvent<Collection> e) {
				grid.getStore().add(e.getObject());
			}
		});
		
		collectionItemsGrid = initializeCollectionItemsGrid();
		
		setWidget(binder.createAndBindUi(this));
		
		// Bind the filter into the grid's store
		gridFilter.bind(grid.getStore());
		
		// Initialize loaders
		RpcProxy<PagingLoadConfig, PagingLoadResult<Collection>> proxy = new RpcProxy<PagingLoadConfig, PagingLoadResult<Collection>>() {
		    @Override
		    public void load(PagingLoadConfig loadConfig, AsyncCallback<PagingLoadResult<Collection>> callback) {
		    	TransactionLoadConfig tConfig = new TransactionLoadConfig(loadConfig);
		    	setFilterInfo(tConfig);
		    	SalesServices.collectionSvc().getList(tConfig, callback);
		    }
	    };
	    
	    loader = new BasicPagingLoader<Collection>(proxy, grid.getStore());
	    loader.addLoadHandler(new LoadResultListStoreBinding<PagingLoadConfig, Collection, PagingLoadResult<Collection>>(grid.getStore()) {
        	@Override
        	public void onLoad(LoadEvent<PagingLoadConfig, PagingLoadResult<Collection>> event) {
        		super.onLoad(event);
        		grid.getSelectionModel().deselectAll();
        		edit(null);
        	}
        });
        
        grid.setLoader(loader);
        grid.setLoadMask(true);
		
		pagingToolbar.bind(loader);
		
		// Initialize the driver, used to run the form editor
		driver.initialize(this);
		
		refNo.addValidator(new MaxLengthValidator(FieldSpecs.REF_NO_MAX));
//		bank.addValidator(new MaxLengthValidator(FieldSpecs.BANK_MAX));
		checkNo.addValidator(new MaxLengthValidator(FieldSpecs.CHECK_NO_MAX));
		cashAccount.addValidator(new MaxLengthValidator(FieldSpecs.CASH_ACCOUNT_MAX));
		
		// Initialize some fields
		PropertyEditor<Date> dateEditor = UI.datePropertyEditor();
		date.setPropertyEditor(dateEditor);
		checkDate.setPropertyEditor(dateEditor);
	}
	
	@Override
	protected SimpleBeanEditorDriver<Collection, ? extends MultiSelectListAndDetailPanel<Collection>> getDriver() {
		return driver;
	}
	
	// UiFactory Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	@Ignore
	@UiFactory
	StoreFilterField<Collection> initializeGridFilter() {
		StoreFilterField<Collection> f = new StoreFilterField<Collection>() {
			@Override
			protected boolean doSelect(Store<Collection> store, Collection parent, Collection item, String filter) {
				String filterInLowerCase = filter.toLowerCase().trim();
				return item.getRefNo().toLowerCase().contains(filterInLowerCase);
			}
		};
		return f;
	}
	
	@UiFactory
	Grid<Collection> createGrid() {
		CollectionProperties props = CollectionProperties.INSTANCE;
		ListStore<Collection> store = new ListStore<Collection>(props.key());
		
		List<ColumnConfig<Collection, ?>> configs = new ArrayList<ColumnConfig<Collection, ?>>();
		
		configs.add(UI.column(props.date(), 50, "Date", UI.dateCell()));
		configs.add(UI.column(props.refNo(), 50, "Ref. No.", new TransactionNoCell<Collection>(store)));
		configs.add(UI.column(props.customerCode(), 100, "Customer"));
		
		AbstractCell<Double> decimalCell = UI.decimalCell();
		
		configs.add(UI.column(props.grossSales(), 70, "Amount", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		configs.add(UI.column(props.subtotal(), 60, "Subtotal", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		configs.add(UI.column(props.tax(), 50, "Tax", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		configs.add(UI.column(props.netAmount(), 70, "Net Amount", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		
		ColumnConfig<Collection, TransactionStatus> statusCol = UI.column(props.status(), 50, "Status", TransactionStatusCell.INSTANCE, HasHorizontalAlignment.ALIGN_CENTER);
		statusCol.setHidden(true);
		configs.add(statusCol);
		
		ColumnModel<Collection> cm = new ColumnModel<Collection>(configs);
		
		GridView<Collection> view = new BasicGridView<Collection>();
		view.setEmptyText("There are no collections to display.");

		Grid<Collection> grid = new Grid<Collection>(store, cm, view);
		grid.setAllowTextSelection(false);
		grid.getSelectionModel().setSelectionMode(SelectionMode.MULTI);
		
		initGridHandlers(grid);
		
		return grid;
	}
	
	// Sales Invoices Grid UI Factory Methods
	
	Grid<CollectionItem> initializeCollectionItemsGrid() {
		CollectionItemProperties props = CollectionItemProperties.INSTANCE;
		
		List<ColumnConfig<CollectionItem, ?>> columns = new ArrayList<ColumnConfig<CollectionItem, ?>>();
		
		AbstractCell<Double> decimalCell = UI.decimalCell();

		columns.add(UI.column(props.salesInvoiceDate(), 80, "Inv. Date.", UI.dateCell()));
		columns.add(UI.column(props.salesInvoiceGroupAndNo(), 80, "Inv. No."));
		columns.add(UI.column(props.grossSales(), 90, "Gross Sales", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.amount(), 90, "Amount", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.overruns(), 80, "Overruns", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.overpayments(), 80, "Overpayments", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.discounts(), 80, "Discounts", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.rejects(), 80, "Rejects", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.returns(), 80, "Returns", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.subtotal(), 90, "Subtotal", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.tax(), 70, "Tax", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.netAmount(), 90, "Net Amount", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		columns.add(UI.column(props.balance(), 80, "Balance", decimalCell, HasHorizontalAlignment.ALIGN_RIGHT));
		
		ListStore<CollectionItem> store = new ListStore<CollectionItem>(props.key());
		
		GridView<CollectionItem> view = new BasicGridView<CollectionItem>();
		view.setEmptyText("There are no collection items to display.");
		
		Grid<CollectionItem> grid = new Grid<CollectionItem>(store, new ColumnModel<CollectionItem>(columns), view);
		
		return grid;
	}
	
	// Logic %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	@Override
	protected void edit(final Collection collection) {
		super.edit(collection);
		
		if (collection != null) {
			Company company = collection.getCompany();
			companyTxt.setValue(company.getCode());
			
			Customer customer = collection.getCustomer();
			customerTxt.setValue(customer.getCode());
			
			paymentTypeTxt.setValue(collection.getPaymentType().toString());
			statusTxt.setValue(collection.getStatus().toString());
			
			// Load the SalesInvoices
			UI.mask(collectionItemsGrid, "Loading Collection Items...");
			SalesServices.collectionSvc().getCollectionItems(collection.getId(), new Callback<List<CollectionItem>>(collectionItemsGrid) {
				@Override
				protected void handleSuccess(List<CollectionItem> items) {
					collectionItemsGrid.getStore().clear();
					collectionItemsGrid.getStore().addAll(items);
				}
			});
		} else {
			companyTxt.setValue("");
			customerTxt.setValue("");
			paymentTypeTxt.setValue("");
			statusTxt.setValue("");
			
			collectionItemsGrid.getStore().clear();
		}
		
		saveBtn.setEnabled(collection != null);
		cancelBtn.setEnabled(collection != null);
	}
	
	// UiHandler Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	@UiHandler("statusMenu")
	void onStatusSelection(SelectionEvent<Item> e) {
		loadData();
	}
	
	@UiHandler("addBtn")
	void addClicked(SelectEvent e) {
		CollectInvoicesWindow cw = new CollectInvoicesWindow();
		Window w = (Window) cw.asWidget();
		w.show();
		w.center();
	}
	
	// Called when the 'Post' button is clicked
	@UiHandler("postBtn")
	void postClicked(SelectEvent e) {
		List<Collection> selected = grid.getSelectionModel().getSelectedItems();
		if (selected.isEmpty()) {
			Notifications.info("Please select at least one collection.");
			return;
		}
		
		UI.mask(mainPanel, "Posting collections...");
		SalesServices.collectionSvc().postCollections(selected, new Callback<List<Collection>>(mainPanel) {
			@Override
			protected void handleSuccess(List<Collection> result) {
				for (Collection collection : result)
					grid.getStore().update(collection);
			}
		});
	}
	
	@UiHandler("moreBtnMenu")
	void onMenuSelection(SelectionEvent<Item> e) {
		Item item = e.getSelectedItem();
		if (item == refreshMenuItem) {
			refreshClicked();
		} else if (item == deleteMenuItem) {
			deleteClicked();
		}
	}
	
	// Called when the 'Refresh' button is clicked
	void refreshClicked() {
		loadData();
	}
	
	// Called when the 'Delete' button is clicked
	void deleteClicked() {
		final Collection selected = grid.getSelectionModel().getSelectedItem();
		if (selected == null)
			return;
		
		if (selected.getStatus() != TransactionStatus.PENDING) {
			Notifications.info("You cannot delete a collection that has already been posted.");
			return;
		}
		
		UI.confirm("Confirm Delete", "Are you sure you want to delete '" + selected.getRefNo() + "'?",
			new ConfirmDialogHandler() {
				@Override
				public void onYes(HideEvent event) {
					UI.mask(mainPanel, "Deleting " + selected.getRefNo() + "...");
					collectionSvc().delete(selected, new Callback<Collection>(mainPanel) {
						@Override
						protected void handleSuccess(Collection result) {
							grid.getStore().remove(result);
							BaseModule.fireEvent(new CollectionDeletedEvent(result));
							
							// Clear the form editor if necessary
							Collection inEditor = driver.flush();
							if (inEditor != null && inEditor.getId().equals(result.getId()))
								edit(null);
						}
					});
				}
			}
		);
	}
	
	// Called when the 'Save' button is clicked
	@UiHandler("saveBtn")
	void saveClicked(SelectEvent e) {
		Collection collection = driver.flush();
		if (driver.hasErrors())
			return;
		
		UI.mask(mainPanel, "Updating collection...");
		collectionSvc().update(collection, new Callback<Collection>(mainPanel) {
			@Override
			protected void handleSuccess(Collection result) {
				grid.getStore().update(result);
				edit(result);
			}
		});
	}
	
	// Called when the 'Cancel' button is clicked
	@UiHandler("cancelBtn")
	void cancelClicked(SelectEvent e) {
		edit(null);
	}

	// ITabItemPanel Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

	@Override
	public String getPanelId() {
		return SalesTasks.COLLECTIONS.getId();
	}

	@Override
	public TabItemConfig getConfig() {
		if (config == null) {
			config = new TabItemConfig("Collections", true);
			config.setIcon(SalesResources.INSTANCE.collections());
		}
		return config;
	}

	@Override
	public void loadData() {
		Log.debug("Loading collections...");
		loader.load();
		edit(null);
	}
	
	// Helper Methods %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
	
	void setFilterInfo(TransactionLoadConfig config) {
		// Status
		if (pendingOnlyMenuItem.isChecked())
			config.setStatuses(TransactionStatus.PENDING);
		else if (postedOnlyMenuItem.isChecked())
			config.setStatuses(TransactionStatus.POSTED);
		else
			config.setStatuses((List<TransactionStatus>) null);
	}

}
